"""
常用函数合集
last update 20240317
"""

import sys
import os
import os.path as OS
import re
import hashlib
import logging
from logging.handlers import QueueHandler, QueueListener


def have_word(regex, text, flags=None):
    '''
    正则查找：
        判断字符串text中是否包含regex
        默认不区分大小写，设置flags为0则区分大小写
    '''
    if flags is None:
        flags = re.I
    if re.compile(regex, flags).search(text) is None:
        return False
    return True


def re_sub_group(regex, repl, text, flags=0):
    '''
    正则替换：
        用repl字符串替换text中regex匹配到的内容，
        如果regex包含子组，则repl只替换子组
        如果regex匹配不到内容，则返回原始字符串
    注：re.sub(pattern, repl, string, count=0, flags=0)
        如果pattern包含子组，不会只替换子组
    '''
    match = re.search(regex, text, flags=flags)
    if match:
        if match.groups():
            result = ""
            start = 0
            for i in range(len(match.groups())):
                result += text[start:match.start(i+1)] + repl
                start =match.end(i+1) # match.end() 是最后一个匹配字符的索引+1
            if start<len(text)-1:
                result += text[start:]
            return result
        else:
            return text.replace(match.group(), repl)
    return text


def calculate_md5(filepath):
    """
    生成路径filepath对应文件的md5值
    """
    with open(filepath, 'rb') as file:
        md5 = hashlib.md5()
        while True:
            data = file.read(4096)
            if not data:
                break
            md5.update(data)
    return md5.hexdigest()
    

def parse_argv(arg):
    '''
    获取运行脚本命令行给定参数（--xxx）对应的值
    如果参数没有给定值，则返回True
    如果找不到参数，返回False
    '''
    if arg in sys.argv:
        next_i = sys.argv.index(arg)+1
        if next_i < len(sys.argv):
            next_str = sys.argv[next_i]
            if not next_str.startswith("-"):
                return next_str
        return True 
    return False


def argv_files(path=None, index=1):
    """
        遍历sys.argv指定的路径的所有文件
        路径默认为-f参数对应的值，如果-f不存在，则默认为sys.argv的第一个值
    """
    if len(sys.argv)>index and path is None:
        path = next((i for i in [parse_argv('-f'), parse_argv('--file')] if i), sys.argv[index])
    if not os.path.exists(path):
        print(f"路径{path}不存在")
        return
        
    if os.path.isfile(path):
        yield path
    else:
        for cur, dirs, files in os.walk(path):
            for file in files:
                yield OS.join(cur, file)

                        
class HmLog:
    isinstance = None
    def __new__(cls, path):
        if cls.isinstance is None:
            with open(path, "w", encoding="utf8") as f:
                f.write("创建log文件\n")
            cls.isinstance = super().__new__(cls)
        return cls.isinstance

    def __init__(self, path, create_file_handler=True, create_ch_handler=False):
        '''
        配置一个单例模式的简单logger
        '''
        self.path = path
        # 创建一个logger
        self.logger = logging.getLogger()
        self.logger.setLevel(logging.INFO)  # Log等级总开关
        # 定义handler的输出格式
        self.formatter = logging.Formatter("[%(asctime)s] [%(levelname)s] [%(filename)s]  [line:%(lineno)d] %(message)s")
        # 创建一个handler，用于写入日志文件
        if create_file_handler:
            self.fh = logging.FileHandler(path, mode='a', encoding="utf8")  # open的打开模式这里可以进行参考
            self.fh.setLevel(logging.DEBUG)  # 输出到file的log等级的开关
            self.fh.setFormatter(self.formatter) 
            # 将logger添加到handler里面
            self.logger.addHandler(self.fh)
        # 创建一个handler，用于输出到控制台
        if create_ch_handler:
            self.ch = logging.StreamHandler()
            self.ch.setLevel(logging.DEBUG)
            # self.ch.setLevel(logging.WARNING)   # 输出到console的log等级的开关
            self.ch.setFormatter(self.formatter)
            self.logger.addHandler(self.ch)
    
    def switch_to_queue(self, q):
        """
        子进程调用, 之后子进程不会往文件里写东西，而且把日志put到q
        注：每个子进程调用这个类，都会重新创建属于它自己的logger，而非单例模式
        """
        if self.logger.handlers:
            self.logger.removeHandler(self.fh)
        # 添加queue handler
        self.qh = QueueHandler(q)
        if not self.logger.handlers: # 不知道为什么，多进程会重复添加，所以加这个判断
            self.qh.setFormatter(self.formatter)
            self.logger.addHandler(self.qh)

    def switch_to_fh(self):
        '''切换回文件handler'''
        if self.logger.handlers:
            self.logger.removeHandler(self.qh)
        if not self.logger.handlers:
            self.logger.addHandler(self.fh)
    
    def start_listen(self, q):
        """
            主进程调用, 接收子进程的q消息，写入制定（如文件）handler
            这是官方推荐的用法，实际上我没有用到，而是在主进程直接读取q的内容再手动写到文件里面
            官方用法里，主进程只创建fh handler, 子进程则只创建queue handler
        """
        # listener是接收q消息并转给它绑定的handler处理
        self.listener = QueueListener(q, self.fh)
        self.listener.start()
        
    def stop_listen(self):
        self.listener.stop()
        
                        
